/*
 * TCSS 558
 * Distributed HashMap
 * Fall 2013
 * James Marquardt
 * Matt Adams
 * Justin Wong
 */
package node.tasks;

import static com.google.common.base.Preconditions.*;
import java.util.List;

import tools.Log;
import model.Entries;
import model.NodeReferences;
import model.NodesAndEntries;
import node.Node;
import node.NodeActual;
import node.NodeRMIProxy;

/**
 * 
 * @author Matt Adams
 */
public class Stabilize implements Runnable {
    
    /**
     * Actual node performing stabilize.
     */
    private NodeActual node;
    
    /**
     * References for node performing stabilize.
     */
    private NodeReferences references;
    
    /**
     * Entries for node performing stabilize.
     */
    private Entries entries;
    
    /**
     * Constructs Stabilize object.
     * 
     * @param node Node to be stabilized.
     * @param references Node references.
     * @param entries Node entries.
     */
    public Stabilize(final NodeActual node, final NodeReferences references, final Entries entries) {
        this.node = checkNotNull(node);
        this.references = checkNotNull(references);
        this.entries = checkNotNull(entries);
    }
    
    @Override
    public void run() {
        final Node successor = this.node.getNodeReferences().getSuccessor();
        Log.out("Stabilizing node with ID: " + this.node.getNodeID());
        if (successor == null) {
            //No successor to notify
            Log.out("Stabilize ended. No successor to notify (null).");
            return;
        }        
        NodesAndEntries successorsNodesAndEntries = null;
        try {
            successorsNodesAndEntries = successor.notifyWithPotentialPredecessor(new NodeRMIProxy(successor.getNodeHostPort(), this.node.getNodeHostPort()));
            Log.out("Successfully retrieved successor's references to its predecessor and successor.");
        } catch (Exception e) {
            Log.err("Error retrieving successor's references during stabilize. Removing reference node: " + successor.getNodeID());
            this.references.removeReference(successor);
            Log.out("Stabilize complete, reference removed successfully.");
            return;
        }
        //Successor's predecessor will be at index 0 of list and the successor will be at index 1.
        final List<Node> successorsReferences = successorsNodesAndEntries.getNodes();
        if (successorsNodesAndEntries.getEntries() != null && !successorsNodesAndEntries.getEntries().isEmpty()) {
            Log.out("Adding references and entries to this node.");
            this.entries.addAll(successorsNodesAndEntries.getEntries());
            successor.addAllEntries(this.node.getEntries());
        }
        if (successorsReferences != null) {
            for (Node node : successorsReferences) {
                this.references.addReference(node);
            }
        }
        try {
        Log.out("Stabilize Finished.");
        if (this.references.getPredecessor() != null) {
            Log.out("Predecessor ID: " + this.references.getPredecessor().getNodeID());
        }
        Log.out("Node ID: " + this.node.getNodeID());
        Log.out("Successor ID " + this.references.getSuccessor().getNodeID());
        System.out.println(this.entries.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
